// SmpWatcherView.cpp : implementation of the CSmpWatcherView class
//

#include "stdafx.h"

#include "csymbolfinder.h"

#include "SmpWatcher.h"

#include "SmpWatcherDoc.h"
#include "SmpWatcherView.h"
#include ".\smpwatcherview.h"
#include "AddWatch.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


TCHAR* CSmpWatcherView::m_pszHdS[]=
{
	_T("Name"),
	_T("Address"),
	_T("Type"),
	_T("Length"),
	_T("Offset"),
	_T("Value"),
	_T("BitPos"),
	_T("BitLen"),
	_T("DimModified")
};

DWORD CSmpWatcherView::m_dwHdWdthS[]=
{
	180,
	100,
	180,
	80,
	80,
	120,
	70,
	70,
	70,
};

DWORD CSmpWatcherView::m_dwHdFlgS[]=
{
	0,
	MCT_CLMN_EDITABLE,
	0,
	0,
	0,
	MCT_CLMN_EDITABLE,
	0,
	0,
	MCT_CLMN_EDITABLE,
};

DWORD CSmpWatcherView::m_dwClmn=sizeof(m_pszHdS)/sizeof(m_pszHdS[0]);

class IMemSpace;
extern IMemSpace* g_pIMemSpace;

// CSmpWatcherView

IMPLEMENT_DYNCREATE(CSmpWatcherView, CMCHTreeView)

BEGIN_MESSAGE_MAP(CSmpWatcherView, CMCHTreeView)
	ON_WM_CREATE()
	ON_WM_CONTEXTMENU()
	ON_NOTIFY(TVN_ENDLABELEDIT, TREE_ID, OnEditEnd)
	ON_NOTIFY(TVN_BEGINLABELEDIT, TREE_ID, OnEditBgn)
	ON_NOTIFY(TVN_GETDISPINFO, TREE_ID, OnTvnGetItemInfo)
	ON_NOTIFY(MCTVN_DELETEITEM, TREE_ID, OnTvnDeleteitem)
	ON_NOTIFY(TVN_ITEMEXPANDING, TREE_ID, OnTvnItemExpand)
	ON_COMMAND(ID_EDIT_ADDWATCH, OnAddwatch)
	ON_COMMAND(ID_EDIT_REFRESH, OnRefresh)
END_MESSAGE_MAP()

// CSmpWatcherView construction/destruction

CSmpWatcherView::CSmpWatcherView()
    :m_pSymbFinder(NULL)
{
	// TODO: add construction code here

}

CSmpWatcherView::~CSmpWatcherView()
{
}

BOOL CSmpWatcherView::PreCreateWindow(CREATESTRUCT& cs)
{
	// TODO: Modify the Window class or styles here by modifying
	//  the CREATESTRUCT cs

	return CMCHTreeView::PreCreateWindow(cs);
}


// CSmpWatcherView diagnostics

#ifdef _DEBUG
void CSmpWatcherView::AssertValid() const
{
	CMCHTreeView::AssertValid();
}

void CSmpWatcherView::Dump(CDumpContext& dc) const
{
	CMCHTreeView::Dump(dc);
}

CSmpWatcherDoc* CSmpWatcherView::GetDocument() const // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CSmpWatcherDoc)));
	return (CSmpWatcherDoc*)m_pDocument;
}
#endif //_DEBUG


// CSmpWatcherView message handlers


int CSmpWatcherView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
//	m_dwCreateStyle|=TVS_CHECKBOXES;
	if (CMCHTreeView::OnCreate(lpCreateStruct) == -1)
		return -1;

	// TODO:  Add your specialized creation code here

	m_cMCTree.SetDimension(m_dwClmn);
	// insert column
	HDITEM hditem;
	hditem.mask = HDI_TEXT | HDI_WIDTH | HDI_FORMAT;
	hditem.fmt = HDF_LEFT | HDF_STRING;

	DWORD dwFlg=0;
	for(DWORD i=0; i<m_dwClmn; i++)
	{
		hditem.pszText = m_pszHdS[i];
		hditem.cxy = m_dwHdWdthS[i];
		dwFlg=m_dwHdFlgS[i];
		InsertMCTClmn(i, &hditem, i, dwFlg);
	}

	return 0;
}

void CSmpWatcherView::OnInitialUpdate()
{
	CMCHTreeView::OnInitialUpdate();

	// TODO: Add your specialized code here and/or call the base class
	m_pSymbFinder=GetDocument()->GetSymbFinder();
}

DWORD CSmpWatcherView::InsertWtch(CString* pcstrType, DWORD dwAddr, CString* pcstrName)
{
	USES_CONVERSION;

        CComPtr<IDiaSymbol> psymbolChild;
        if(m_pSymbFinder->FindSymbol(pcstrName.GetBuffer(), &psymbolChild, SymTagData))
        {
        }
        else if(m_pSymbFinder->FindSymbol(pcstrType.GetBuffer(), &psymbolChild))
        {
        }
        else if()
        {
        }
	CComPtr<IDiaEnumSymbols> pEnum;
	CStringW cstrwFind(*pcstrType);
	DWORD ret=0;

	DWORD  dwChldCnt;
	enum SymTagEnum enTpS[]={SymTagUDT, SymTagTypedef, SymTagData, SymTagEnum, SymTagBaseType};
	for(DWORD i=0; i<sizeof(enTpS)/sizeof(enTpS[0]); i++)
	{
		if( (SUCCEEDED(m_pglobal->findChildren(enTpS[i], cstrwFind.GetBuffer(), nsfCaseInsensitive|nsfRegularExpression, &pEnum)))
			&& (pEnum!=NULL) ) // sometimes even findchidren success, but the pEnum is null!!!
		{
			DWORD celt;
			for( dwChldCnt=0;
				SUCCEEDED( pEnum->Next( 1, &psymbolChild, &celt ) ) && celt == 1;
				dwChldCnt++) 
			{
				//
				// prepare CDIAViewItem
				CDIAViewItem* pDiaVI;
				if(SymTagData==enTpS[i])
				{
					pDiaVI=new CDIAViewItem(psymbolChild, NULL, dwAddr);
				}
				else
				{
					pDiaVI=new CDIAViewItem(NULL, psymbolChild, dwAddr);
				}

				if(INVALID_ADDR!=dwAddr)
				{
					pDiaVI->SetBaseAddr(dwAddr, TRUE);
				}

				// update CDIAViewItem value
				if(NULL!=g_pIMemSpace)
					pDiaVI->MemUpdateLocVal(g_pIMemSpace);

				
				InsertSymbol(pDiaVI, TVI_ROOT, TVI_LAST, FALSE, TRUE);

				psymbolChild=NULL;	//1 must release the psymbolChild befere reuse it!!!
				ret++;
			}
			pEnum=NULL;
		}
	}

	return ret;
}

HTREEITEM CSmpWatcherView::InsertSymbol(CDIAViewItem* pDiaVI, HTREEITEM hParent, HTREEITEM hInsertAfter, BOOL fExpandPtr, BOOL fRecurse)
{
	USES_CONVERSION;
	CDIAData* pdiaData=pDiaVI->m_pdiadata.get();
	
	CWtchViewItem* pWtchItem=new CWtchViewItem(pDiaVI);

	CString cstrName;
	pdiaData->GetDataName(&cstrName);
	TVINSERTSTRUCT tviItem;
	tviItem.hParent=hParent;
	tviItem.hInsertAfter=hInsertAfter;
	memset(&tviItem.item, 0, sizeof(tviItem.item));
	tviItem.item.mask=(TVIF_TEXT|TVIF_PARAM|TVIF_CHILDREN);
	tviItem.item.pszText=cstrName.GetBuffer();
	tviItem.item.cChildren=I_CHILDRENCALLBACK;
	tviItem.item.lParam=reinterpret_cast<LPARAM>(pWtchItem);
	HTREEITEM hitem=m_cMCTree.InsertItem(&tviItem);

	RefreshSubItem(hitem);

	if(fRecurse)
	{
		InsertChild(hitem, pWtchItem, fExpandPtr, fRecurse);
	}

	return hitem;
}

VOID CSmpWatcherView::RefreshSubItem(HTREEITEM hItem)
{
	CWtchViewItem* pWtchItem=reinterpret_cast<CWtchViewItem*>(m_cMCTree.GetItemData(hItem));
	CDIAViewItem* pDiaVI=pWtchItem->pdiaVI.get();
	
	DWORD dwClmn=m_dwClmn-1;	// -1 no the first column
	CString* pcstrIns=new CString[dwClmn];
	DWORD* pdwIns=new DWORD[dwClmn];

	pdwIns[0]=1;
	DWORD dwAddr;
	pDiaVI->GetAddr(&pcstrIns[0]);

	pdwIns[1]=2;
	pDiaVI->GetTypeName(pcstrIns+1);

	pdwIns[2]=3;
	pDiaVI->GetLen(pcstrIns+2);

	pdwIns[3]=4;
	pDiaVI->GetOffset(pcstrIns+3);
	
	pdwIns[4]=5;
	pDiaVI->GetValue(pcstrIns+4);

	pdwIns[5]=6;
	pdwIns[6]=7;
	pDiaVI->GetBitInfo(pcstrIns+5, pcstrIns+6);

	m_cMCTree.SetSubItemS(hItem, dwClmn, pdwIns, pcstrIns);
	delete[] pcstrIns;
	delete[] pdwIns;
}


void CSmpWatcherView::OnTvnItemExpand(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMTREEVIEW pnmtv=reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
	TVITEM* ptvi=&pnmtv->itemNew;
	CWtchViewItem* pWtchItem=reinterpret_cast<CWtchViewItem*>(m_cMCTree.GetItemData(ptvi->hItem));
	InsertChild(ptvi->hItem, pWtchItem, TRUE, FALSE);
}

void CSmpWatcherView::InsertChild(HTREEITEM hItem, CWtchViewItem* pWtchItem, BOOL fExpandPtr, BOOL fRecurs)
{
	CWaitCursor waitCursor;

	if(FALSE!=pWtchItem->fExpand)
	{
		return;
	}


	CDIADataWtchEnum* pEnum;
	BOOL fPtr;
	if(false==pWtchItem->pdiaVI->EnumChild(&pEnum, &fPtr))
	{
		pWtchItem->dwChldNum=0;
		pWtchItem->fExpand=true;
		return;
	}

	if(fExpandPtr || (!fPtr) )
	{
		DWORD dwAddr;
		if(FALSE==pWtchItem->pdiaVI->GetAddr(&dwAddr))
		{dwAddr=INVALID_ADDR;}
			

		CDIAViewItem* pWtchObj;
		int i;
		for(i=0;
			pEnum->Next(&pWtchObj);
			i++)
		{
			if(INVALID_ADDR!=dwAddr)
				{pWtchObj->SetBaseAddr(dwAddr);}
			
			if(NULL!=g_pIMemSpace)
				{pWtchObj->MemUpdateLocVal(g_pIMemSpace);}

			InsertSymbol(pWtchObj, hItem, TVI_LAST, fExpandPtr, fRecurs);
		}
		pWtchItem->dwChldNum=i;
		pWtchItem->fExpand=true;
	}

	delete pEnum;
	return;
}



void CSmpWatcherView::OnTvnGetItemInfo(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMTVDISPINFO pNMTVDISPINFO=reinterpret_cast<LPNMTVDISPINFO>(pNMHDR);
	TVITEM* ptvitem=&(pNMTVDISPINFO->item);

	if((TVIF_CHILDREN)&ptvitem->mask)
	{
		CWtchViewItem* pWtchItem=reinterpret_cast<CWtchViewItem*>(m_cMCTree.GetItemData(ptvitem->hItem));
		if(NULL!=pWtchItem)
		{
			if(FALSE!=pWtchItem->fExpand)
			{
				ptvitem->cChildren=pWtchItem->dwChldNum;
				*pResult=1;
				return;
			}
		}
		else
		{
			ptvitem->cChildren=0;
			*pResult=1;
			return;
		}
	}
}


void CSmpWatcherView::OnTvnDeleteitem(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);

	CWaitCursor waitCursor;

	struct CWtchViewItem *pitem=reinterpret_cast<CWtchViewItem*>(pNMTreeView->itemOld.lParam);
	if(NULL!=pitem){delete pitem;}
	*pResult = 0;
	return ;
}

void CSmpWatcherView::OnContextMenu(CWnd* pWnd, CPoint point)
{
	// TODO: Add your message handler code here
	HMENU hmenu;            // menu template          
	HMENU hmenuTrackPopup;  // shortcut menu   

	//  Load the menu template containing the shortcut menu from the 
	//  application's resources. 
	hmenu = LoadMenu(AfxGetApp()->m_hInstance, MAKEINTRESOURCE(IDR_MENU_INSDEL)); 
	if (hmenu == NULL) 
		return; 

	// Get the first shortcut menu in the menu template. This is the 
	// menu that TrackPopupMenu displays. 
	hmenuTrackPopup = GetSubMenu(hmenu, 0); 

	// Draw and track the shortcut menu.  
	int iCmd=TrackPopupMenu(hmenuTrackPopup, TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_RETURNCMD, 
		point.x, point.y, 0, pWnd->m_hWnd, NULL); 

	// Destroy the menu. 
	DestroyMenu(hmenu); 

	// insert item
	if(ID__INSERT==iCmd)
	{
		OnAddwatch();
	}
	// delete item
	else if(ID__DELETE==iCmd)
	{
		HTREEITEM htrItem=m_cMCTree.GetSelectedItem();

		if(NULL!=htrItem)
		{
			if(NULL==m_cMCTree.GetParentItem(htrItem))
			{
				m_cMCTree.DeleteItem(htrItem);
				return;
			}
		}
	}
}

//*pResult: Returns TRUE to cancel label editing
void CSmpWatcherView::OnEditBgn(NMHDR *pNMHDR, LRESULT *pResult)
{
	NMTVDISPINFO* pnmInfo= reinterpret_cast<NMTVDISPINFO*>(pNMHDR);

	// check the mask we need
	if((TVIF_HANDLE|TVIF_TEXT|MCTVIF_EDITSUBID|TVIF_PARAM)
		!=((TVIF_HANDLE|TVIF_TEXT|MCTVIF_EDITSUBID|TVIF_PARAM)&pnmInfo->item.mask))
	{
		TRACE(_T("EditFilter data field is invalid\r\n"));
		*pResult=0;
		return;
	}

	*pResult=0;
	CWtchViewItem* pitem=reinterpret_cast<CWtchViewItem*>(pnmInfo->item.lParam);

	DWORD dwSubId;
	m_cMCTree.GetEditSubId(&dwSubId);
	// address
	if(1==dwSubId)
	{
		HTREEITEM htreeitem=m_cMCTree.GetParentItem(pnmInfo->item.hItem);
		//root
		if(NULL==htreeitem)
		{
			return;
		}
		//*ptr
		else
		{
			CWtchViewItem* pitemParent=reinterpret_cast<CWtchViewItem*>(m_cMCTree.GetItemData(htreeitem));
			if(FALSE!=pitemParent->pdiaVI->m_pdiadata->IsPtr())
			{
				return;
			}
		}
		//static
		if(FALSE!=pitem->pdiaVI->m_pdiadata->Is_static())
		{
			return;
		}

	}
	// value
	else if(5==dwSubId)
	{
		//base type
		if(	   (FALSE!=pitem->pdiaVI->m_pdiadata->IsBaseType())
			|| (FALSE!=pitem->pdiaVI->m_pdiadata->IsPtr())	)
		{
			return;
		}
	}
	else
	{
		TRACE(_T("sub id:%d should not be editable!\r\n"), dwSubId);
		*pResult=0;
		ASSERT(FALSE);
	}
	*pResult=1;
}

void CSmpWatcherView::OnEditEnd(NMHDR *pNMHDR, LRESULT *pResult)
{
	NMTVDISPINFO* pnmInfo= reinterpret_cast<NMTVDISPINFO*>(pNMHDR);
	CWtchViewItem* pitem=reinterpret_cast<CWtchViewItem*>(pnmInfo->item.lParam);

	CWaitCursor waitCursor;

	*pResult=1;
	DWORD dwSubId;
	m_cMCTree.GetEditSubId(&dwSubId);
	CEdit* pcedit=m_cMCTree.GetEditControl();
	CString cstredit;
	pcedit->GetWindowText(cstredit);
	// address
	if(1==dwSubId)
	{
		DWORD dwBaseAddr;

		if(1==sscanf(cstredit.GetBuffer(), _T("0x%08x"), &dwBaseAddr))
		{
			if(FALSE!=UpdateAddr(pnmInfo->item.hItem, dwBaseAddr, FALSE, g_pIMemSpace))
			{
				return;
			}
		}
	}
	// value
	else if(5==dwSubId)
	{
		// update the value
		CString cstrUpdate;
		if(FALSE!=UpdateVal(pitem->pdiaVI.get(), &cstredit, &cstrUpdate))
		{
			pcedit->SetWindowText(cstrUpdate);

			// update memspace
			if(NULL!=g_pIMemSpace)
			{
				pitem->pdiaVI->LocValUpdateMem(g_pIMemSpace);
			}
			return;
		}
	}
	else
	{
		TRACE(_T("sub id:%d should not be editable!\r\n"), dwSubId);
		*pResult=0;
		ASSERT(FALSE);
	}
	*pResult=0;
}

void CSmpWatcherView::OnAddwatch()
{
	// TODO: Add your command handler code here
	CDlgAddWatch cDlaAddWtch;
	INT_PTR nRet=cDlaAddWtch.DoModal();
	if(IDOK!=nRet)
	{
		return;
	}

	InsertWtch(&cDlaAddWtch.m_cstrType, cDlaAddWtch.m_dwAddr, &cDlaAddWtch.m_cstrName);
}

void CSmpWatcherView::OnRefresh()
{
	m_cMCTree.ForEach(CMCTreeCtrl::FOREACH_TYPE_DEEP, RefreshForEach, this);
}

BOOL CSmpWatcherView::RefreshForEach(HTREEITEM hItem)
{
	TCHAR buf[MAX_PATH];
	DWORD dwLen;
	CWtchViewItem* pWtchItem=reinterpret_cast<CWtchViewItem*>(m_cMCTree.GetItemData(hItem));
	CDIAViewItem* pDDW=pWtchItem->pdiaVI.get();

/*	
	buf[0]=0;
	dwLen=sizeof(buf)/sizeof(buf[0]);
	m_cMCTree.GetSubItem(hItem, 5, buf, &dwLen);	// value
	if(0!=buf[0])
	{
		DWORD dwAddr=(DWORD)(-1);
		if(NULL==_tcsstr(buf, _T("0x")))
		{
			sscanf(buf, _T("%d"), &dwAddr);
		}
		else
		{
			sscanf(buf, _T("0x%x"), &dwAddr);
		}
		pDDW->SetAddr(dwAddr);
	}
*/	
	buf[0]=0;
	dwLen=sizeof(buf)/sizeof(buf[0]);
	m_cMCTree.GetSubItem(hItem, 5, buf, &dwLen);	// value
	// update CDIAViewItem value
	if(NULL!=g_pIMemSpace)
	{
		pDDW->MemUpdateLocVal(g_pIMemSpace);
	}

	RefreshSubItem(hItem);

	return TRUE;
}

BOOL CSmpWatcherView::UpdateAddr(HTREEITEM htreeitem, DWORD dwAddr, BOOL fBaseAddr, IMemSpace* pIMemSpace)
{
	CWtchViewItem* pitem=reinterpret_cast<CWtchViewItem*>(m_cMCTree.GetItemData(htreeitem));

/*
	// const no address
	if(FALSE!=pitem->pdiaDataWtch->m_pdiadata->IsConst())
	{
		return FALSE;
	}
*/	
	// set address
	if(FALSE==fBaseAddr)
	{
		pitem->pdiaVI->SetAddr(dwAddr);
	}
	// set base address
	else
	{
		if(FALSE==pitem->pdiaVI->SetBaseAddr(dwAddr))
			{return FALSE;}
		// get the address of data
		pitem->pdiaVI->GetAddr(&dwAddr);
	}

	CString cstrAddr;
	cstrAddr.Format(_T("0x%08x"), dwAddr);
	m_cMCTree.SetSubItem(htreeitem, 1, cstrAddr.GetBuffer());
	
	CString cstrVal;
	UpdateVal(pitem->pdiaVI.get(), pIMemSpace, &cstrVal);
	m_cMCTree.SetSubItem(htreeitem, 5, cstrVal.GetBuffer());

	// pointer static don't update child
	if( (pitem->pdiaVI->m_pdiadata->IsPtr()) || (pitem->pdiaVI->m_pdiadata->Is_static()) )
	{
		return TRUE;
	}

	//update child
	HTREEITEM hChildItem = m_cMCTree.GetChildItem(htreeitem);
	if(NULL!=hChildItem)
	{
		HTREEITEM hNextItem;
		while (hChildItem != NULL)
		{
			UpdateAddr(hChildItem, dwAddr, TRUE, pIMemSpace);
			hNextItem = m_cMCTree.GetNextItem(hChildItem, TVGN_NEXT);
			hChildItem = hNextItem;
		}
	}

	return TRUE;
}

BOOL CSmpWatcherView::UpdateVal(CDIAViewItem* pDDW, IMemSpace* pIMemSpace, CString* pcstrDisVal)
{
	ASSERT(NULL!=pDDW);
	
	// update value
	if(NULL!=pIMemSpace)
	{
		if(FALSE!=pDDW->MemUpdateLocVal(pIMemSpace))
		{
			// get value display
			if( (NULL!=pcstrDisVal) && (NULL!=pDDW->m_pDataVal.get()) )
				{pDDW->m_pDataVal->Val2CStr(pcstrDisVal);};
			return TRUE;
		}
	}
	return FALSE;
}

BOOL CSmpWatcherView::UpdateVal(CDIAViewItem* pDDW, CString* pcstrUpdateVal, CString* pcstrDisVal)
{
	ASSERT(NULL!=pDDW);
	
	// update value
	if( (NULL!=pcstrUpdateVal) && (NULL!=pDDW->m_pDataVal.get()) )
	{
		if(FALSE!=pDDW->m_pDataVal->CStr2Val(pcstrUpdateVal))
		{
			// get value display
			if(NULL!=pcstrDisVal){pDDW->m_pDataVal->Val2CStr(pcstrDisVal);};
			return TRUE;
		}
	}
	return FALSE;
}



